#include "Camera.h"


//	the complete camera model can be found in the chapter 4 of the book "realistic ray tracing"
//	center: the center of the camera coordinates
//	corner: the vector of the lower left corner of the image in the camera coordinates
//	across: the vector indicates the horizontal edge of the image
//	up: the vector indicates the vertical edge of the image
//	uvw: coordinates of the camera model
//		u: align the horizontal edge of the image
//		v: align the vertical edge of the image
//		w: the normal of the image plane but point to the film rather than the image
//	d: the distance of the image and center
//	lens_radius: the radius of the aperture
//	here we assume w cross the center of the film and the image
//	i.e., u0 + u1 = 0; v0 + v1 = 0
Camera::Camera()
{

}

Camera::Camera(const Camera& origin)
{
	center = origin.center;
	corner = origin.corner;
	across = origin.across;
	up = origin.up;
	uvw = origin.uvw;
	u0 = origin.u0;
	u1 = origin.u1;
	v0 = origin.v0;
	v1 = origin.v1;
	d = origin.d;
	lens_radius = origin.lens_radius;
}

Camera::Camera(Vector3 c, Vector3 gaze, Vector3 vup, float aperture, float left, float right, float bottom, float top, float distance)
	: center(c), u0(left), u1(right), v0(bottom), v1(top), d(distance)
{
	lens_radius = aperture / 2.0f;
	uvw.initFromWV(-gaze, vup);
	corner = center + u0 * uvw.u() + v0 * uvw.v() - d * uvw.w();
	across = (u1 - u0) * uvw.u();
	up = (v1 - v0) * uvw.v();
}

//	a and b are the pixel position in [0, 1)
//	xi1 and xi2 are the lens samples in the range [0, 1)
//	we have to transform the samples into the circle with radius = lens_radius
//	here we use the inverse method to sample uniformly in the circle

//	the procedure for the camera with lens is the same as the pinhole camera
//	we send rays from the random point in the lens, pointing at a random position in the pixel
//	of the image plane
//	to understand this, just note that all rays hitting ONE specific point in the film must have
//	comes across ONE specific point in the image plane
//	so to compute the value of a specific pixel in the film, we need to send multiple rays
//	from the pixel to the lens, then the lens 'converge' them into the specific point in the image plane,
//	as a result, to evaluate the pixel value, it is the same to send rays from lens and to the specific point
//	in the image plane, so, we can 'reflect' the pixel from the film into the image
Ray Camera::getRay(float a, float b, float xi1, float xi2)
{
	float r, theta;
	r = lens_radius * sqrt(xi1);
	theta = xi2 * PI * 2;
	Vector3 origin = center + r * cos(theta) * uvw.u() + r * sin(theta) * uvw.v();
	Vector3 target = corner + across * a + up * b;
	return Ray(origin, unitVector(target - origin));
}

void Camera::setCenter(Vector3 _center)
{
	center = _center;
	corner = center + u0 * uvw.u() + v0 * uvw.v() - d * uvw.w();
}

void Camera::setGaze(Vector3 _gaze)
{
	uvw.initFromWV(-_gaze, uvw.v());
	corner = center + u0 * uvw.u() + v0 * uvw.v() - d * uvw.w();
	across = (u1 - u0) * uvw.u();
	up = (v1 - v0) * uvw.v();
}

void Camera::setVUp(Vector3 _up)
{
	uvw.initFromWV(uvw.w(), _up);
	corner = center + u0 * uvw.u() + v0 * uvw.v() - d * uvw.w();
	across = (u1 - u0) * uvw.u();
	up = (v1 - v0) * uvw.v();
}

void Camera::setDistance(float _d)
{
	d = _d;
	corner = center + u0 * uvw.u() + v0 * uvw.v() - d * uvw.w();
}

void Camera::setAperture(float _aperture)
{
	lens_radius = _aperture / 2.0f;
}

void Camera::setTmin(float _tmin)
{
	tmin = _tmin;
}

void Camera::setTmax(float _tmax)
{
	tmax = _tmax;
}

Vector3 Camera::getCenter()
{
	return center;
}

Vector3 Camera::getGaze()
{
	return -uvw.w();
}

Vector3 Camera::getVUp()
{
	return uvw.v();
}

float Camera::getDistance()
{
	return d;
}

float Camera::getAperture()
{
	return 2.0f * lens_radius;
}

float Camera::getTmin()
{
	return tmin;
}

float Camera::getTmax()
{
	return tmax;
}